package hos.map.tool;

/**
 * <p>Title: MathUtil </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2023-12-06 22:03
 */
public class MathUtil {

    private MathUtil() {
    }

    public static double round(final double value) {
        return round(value, 6);
    }

    public static double round(final double value, int decimals) {
        return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
    }

    public static double degToRadian(final double deg) {
        return deg * (getPI() / 180.0);
    }

    public static double radianToDeg(final double rad) {
        return rad * (180.0 / getPI());
    }

    public static double getEccentricityRatio() {
        return Double.parseDouble("0.00669342162296594323");
    }

    public static double getEarthRadius() {
        return Double.parseDouble("6378245.0");
    }
    public static double getPolarRadius() {
        return Double.parseDouble("6356752.314245");
    }

    public static double getPI() {
        return Double.parseDouble("3.14159265358979323846");
    }

    public static double getPIX() {
        return getPI() * 3000.0 / 180.0;
    }

    public static double toRadians(double degrees) {
        return degrees / 180.0 * getPI();
    }

    public static double toDegrees(double rad) {
        return rad * (180.0 / getPI());
    }

    public static double circleArea(int radius) {
        return getPI() * radius * radius;
    }

    public static double circleDistance(int radius) {
        return 2 * getPI() * radius;
    }

    /**
     * /// Convert a bearing to be within the 0 to +360 degrees range.
     * /// Compass bearing is in the rangen 0° ... 360°
     */
    public static double normalizeBearing(final double bearing) {
        return (bearing + 360) % 360;
    }

    /// Pads a number with a single zero, if it is less than 10
    public static String zeroPad(Number number) {
        return (number.doubleValue() < 10 ? "0" : "") + number;
    }

    /**
     * /// Converts a decimal coordinate value to sexagesimal format
     * ///
     * ///     final String sexa1 = decimal2sexagesimal(51.519475);
     * ///     expect(sexa1, '51° 31\' 10.11"');
     * ///
     * ///     final String sexa2 = decimal2sexagesimal(-42.883891);
     * ///     expect(sexa2, '42° 53\' 02.01"');
     */
    public static String decimal2sexagesimal(final double dec) {
        final StringBuilder buf = new StringBuilder();

        final double absDec = Math.abs(dec);
        final int deg = (int) Math.floor(absDec);
        buf.append(deg + "°");
        final double minis = (absDec - deg) * 60.0;
        final int min = (int) Math.floor(minis);
        buf.append(zeroPad(min)).append("′");

        final double secs = (minis - Math.floor(minis)) * 60.0;
        final int sec = (int) Math.floor(secs);
        final int franc = (int) ((secs - Math.floor(secs)) * 100.0);
        buf.append(zeroPad(sec)).append(".").append(zeroPad(franc)).append("″");

        return buf.toString();
    }

    /**
     * double 位置 转换成  分 秒 sexagesimal2decimal('51° 31\' 10.11"')
     */
    public static double sexagesimal2decimal(String data) {
        if (data == null || data.length() == 0) {
            return 0;
        }
        if (!(data.contains("°") && data.contains("′") && data.contains("″"))) {
            return 0;
        }
        String duStr = data.split("°")[0];
        String fenStr = data.split("′")[0].split("°")[1];
        String miaoStr = data.split("′")[1].split("″")[0];
        return ((Double.parseDouble(miaoStr) / 60) + Double.parseDouble(fenStr)) / 60 + Double.parseDouble(duStr);
    }

    /// Restrict x to the range [low, high].
    public static double clamp(double x, double low, double high) {
        return x < low ? low : (Math.min(x, high));
    }


    /// Wraps the given value into the inclusive-exclusive interval between min
    /// and max.
    /// @param n   The value to wrap.
    /// @param min The minimum.
    /// @param max The maximum.
    public static double wrap(double n, double min, double max) {
        return (n >= min && n < max) ? n : (mod(n - min, max - min) + min);
    }


    /// Returns the non-negative remainder of x / m.
    /// @param x The operand.
    /// @param m The modulus.
    public static double mod(double x, double m) {
        return ((x % m) + m) % m;
    }

    /// Returns mercator Y corresponding to latitude.
    /// See http://en.wikipedia.org/wiki/Mercator_projection .
    public static double mercator(double lat) {
        return Math.log(Math.tan(lat * 0.5 + getPI() / 4));
    }

    /// Returns latitude from mercator Y.
    public static double inverseMercator(double y) {
        return 2 * Math.atan(Math.exp(y)) - getPI() / 2;
    }

    /// Returns haversine(angle-in-radians).
    /// hav(x) == (1 - cos(x)) / 2 == sin(x / 2)^2.
    public static double hav(double x) {
        return Math.sin(x * 0.5) * Math.sin(x * 0.5);
    }

    /// Computes inverse haversine. Has good numerical stability around 0.
    /// arcHav(x) == acos(1 - 2 * x) == 2 * asin(sqrt(x)).
    /// The argument must be in [0, 1], and the result is positive.
    public static double arcHav(double x) {
        return 2 * Math.asin(Math.sqrt(x));
    }

    // Given h==hav(x), returns sin(abs(x)).
    public static double sinFromHav(double h) {
        return 2 * Math.sqrt(h * (1 - h));
    }

    // Returns hav(asin(x)).
    public static double havFromSin(double x) {
        return (x * x) / (1 + Math.sqrt(1 - (x * x))) * .5;
    }

    // Returns sin(arcHav(x) + arcHav(y)).
    public static double sinSumFromHav(double x, double y) {
        final double a = Math.sqrt(x * (1 - x));
        final double b = Math.sqrt(y * (1 - y));
        return 2 * (a + b - 2 * (a * y + b * x));
    }

    /// Returns hav() of distance from (lat1, lng1) to (lat2, lng2) on the unit
    /// sphere.
    public static double havDistance(double lat1, double lat2, double dLng) {
        return hav(lat1 - lat2) + hav(dLng) * Math.cos(lat1) * Math.cos(lat2);
    }

}
